export class ToyVue {
  constructor(config) {
    this.template = document.querySelector(config.el);
    this.data = reactive(config.data);

    for (let name in config.methods) {
      this[name] = () => {
        config.methods[name].apply(this.data);
      };
    }

    this.traversal(this.template);
  }

  traversal(node) {
    if (node.nodeType === Node.TEXT_NODE) {
      if (node.textContent.trim().match(/^{{([\s\S]+)}}$/)) {
        let name = RegExp.$1.trim();
        effect(() => node.textContent = this.data[name]);
      }
    }

    if (node.nodeType === Node.ELEMENT_NODE) {
      let attributes = node.attributes;
      for (let attribute of attributes) {
        if (attribute.name === 'v-model') {
          let name = attribute.value;
          // 每次渲染后，把数据变化后的处理函数保存起来，然后立即执行一次处理函数
          effect(() => node.value = this.data[name]);
          node.addEventListener('input', event => this.data[name] = node.value);
        }
        if (attribute.name.match(/^v\-bind:([\s\S]+)$/)) {
          let attrName = RegExp.$1;
          let name = attribute.value;
          effect(() => node.setAttribute(attrName, this.data[name]));
        }
        if (attribute.name.match(/^v\-on:([\s\S]+)$/)) {
          let eventName = RegExp.$1;
          let fnName = attribute.value;
          node.addEventListener(eventName, this[fnName]);
        }
      }
    }

    if (node.childNodes && node.childNodes.length) {
      for (let child of node.childNodes)
        this.traversal(child);
    }
  }
}

let effects = new Map();
let currentEffect = null;

function effect(fn) {
  currentEffect = fn;
  fn();
  currentEffect = null;
}

function reactive(obj) {
  let observed = new Proxy(obj, {
    get(obj, prop) {
      if (currentEffect) {
        if (!effects.has(obj))
          effects.set(obj, new Map);
        if (!effects.get(obj).has(prop))
          effects.get(obj).set(prop, new Array);

        effects.get(obj).get(prop).push(currentEffect);
      }
      return obj[prop];
    },
    set(obj, prop, value) {
      obj[prop] = value;

      if (effects.has(obj) && effects.get(obj).has(prop)) {
        for (let effect of effects.get(obj).get(prop)) {
          effect();// 只要发现数据变化了，就执行一次当初存入的方法
        }
      }
      return true;
    }
  });
  return observed;
}

// const counter = reactive({num: 0});
// window.counter = counter;
// // effect: 数据监听器, 数据发生变化后执行对应的函数参数
// effect(() => (alert(counter.num)));